﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Fwql.Net.Utils;

namespace Fwql.Net.Extensions
{
    public static class EnumerableExtension
    {
        /// <summary>
        /// 转换集合中所有项目为指定类型
        /// </summary>
        /// <typeparam name = "TSource"></typeparam>
        /// <typeparam name = "TTarget"></typeparam>
        /// <param name = "source"></param>
        /// <returns></returns>
        /// <example>
        /// 	var values = new[] { "1", "2", "3" };
        /// 	values.ConvertList&lt;string, int&gt;();
        /// </example>
        public static IEnumerable<TTarget> ConvertList<TSource, TTarget>(this IEnumerable<TSource> source)
        {
            return source.Select(value => value.ConvertTo<TTarget>());
        }

        /// <summary>
        /// 对集合中所有项目进行一次相同的操作
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "values"></param>
        /// <param name = "action"></param>
        /// <example>
        /// 	var values = new[] { "1", "2", "3" };
        /// 	values.ForEach(Console.WriteLine);
        /// </example>
        public static void ForEach<T>(this IEnumerable<T> values, Action<T> action)
        {
            foreach (var value in values)
                action(value);
        }

        /// <summary>
        /// 对集合中所有项目进行一次相同的操作
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "values"></param>
        /// <param name = "action"></param>
        public static void ForEach<T>(this IEnumerable<T> values, Action<T, int> action)
        {
            int index = 0;
            foreach (T item in values)
                action(item, index++);
        }

        ///<summary>
        ///	过滤集合中所有null项，如是集合本身为null，则返回空IEnumerable
        ///</summary>
        ///<typeparam name = "T"></typeparam>
        ///<param name = "target"></param>
        ///<example>
        ///	object[] items = new object[]{ null, "Hello World!", null, "Good bye!" };
        ///	foreach(var item in items.IgnoreNulls()){
        ///	    // ...
        ///	}
        ///</example>
        public static IEnumerable<T> IgnoreNulls<T>(this IEnumerable<T> target)
        {
            if (ReferenceEquals(target, null))
                yield break;

            foreach (var item in target.Where(item => !ReferenceEquals(item, null)))
                yield return item;
        }

        /// <summary>
        /// 返回集合中最大项
        /// </summary>
        /// <typeparam name = "TItem">集合类型</typeparam>
        /// <typeparam name = "TValue"></typeparam>
        /// <param name = "items"></param>
        /// <param name = "selector">选择器</param>
        /// <param name = "maxValue"></param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		int age;
        /// 		var oldestPerson = persons.MaxItem(p =&gt; p.Age, out age);
        /// 	</code>
        /// </example>
        public static TItem MaxItem<TItem, TValue>(this IEnumerable<TItem> items,
                                                   Func<TItem, TValue> selector,
                                                   out TValue maxValue)
            where TItem : class
            where TValue : IComparable
        {
            TItem maxItem = null;
            maxValue = default(TValue);

            foreach (var item in items)
            {
                if (item == null)
                    continue;

                var itemValue = selector(item);

                if ((maxItem != null) && (itemValue.CompareTo(maxValue) <= 0))
                    continue;

                maxValue = itemValue;
                maxItem = item;
            }

            return maxItem;
        }

        /// <summary>
        /// 返回集合中最大项
        /// </summary>
        /// <typeparam name = "TItem">集合类型</typeparam>
        /// <typeparam name = "TValue"></typeparam>
        /// <param name = "items"></param>
        /// <param name = "selector">选择器</param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		var oldestPerson = persons.MaxItem(p =&gt; p.Age);
        /// 	</code>
        /// </example>
        public static TItem MaxItem<TItem, TValue>(this IEnumerable<TItem> items, Func<TItem, TValue> selector)
            where TItem : class
            where TValue : IComparable
        {
            TValue maxValue;

            return items.MaxItem(selector, out maxValue);
        }

        /// <summary>
        /// 返回集合中最小项
        /// </summary>
        /// <typeparam name = "TItem">集合类型</typeparam>
        /// <typeparam name = "TValue"></typeparam>
        /// <param name = "items"></param>
        /// <param name = "selector">选择器</param>
        /// <param name = "minValue"></param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		int age;
        /// 		var youngestPerson = persons.MinItem(p =&gt; p.Age, out age);
        /// 	</code>
        /// </example>
        public static TItem MinItem<TItem, TValue>(this IEnumerable<TItem> items,
                                                   Func<TItem, TValue> selector,
                                                   out TValue minValue)
            where TItem : class
            where TValue : IComparable
        {
            TItem minItem = null;
            minValue = default(TValue);

            foreach (var item in items)
            {
                if (item == null)
                    continue;
                var itemValue = selector(item);

                if ((minItem != null) && (itemValue.CompareTo(minValue) >= 0))
                    continue;
                minValue = itemValue;
                minItem = item;
            }

            return minItem;
        }

        /// <summary>
        /// 返回集合中最小项
        /// </summary>
        /// <typeparam name = "TItem">集合类型</typeparam>
        /// <typeparam name = "TValue"></typeparam>
        /// <param name = "items"></param>
        /// <param name = "selector">选择器</param>
        /// <returns></returns>
        /// <example>
        /// 	<code>
        /// 		var youngestPerson = persons.MinItem(p =&gt; p.Age);
        /// 	</code>
        /// </example>
        public static TItem MinItem<TItem, TValue>(this IEnumerable<TItem> items, Func<TItem, TValue> selector)
            where TItem : class
            where TValue : IComparable
        {
            TValue minValue;

            return items.MinItem(selector, out minValue);
        }

        ///<summary>
        ///	Distinct
        ///</summary>
        ///<param name = "source"></param>
        ///<param name = "expression"></param>
        ///<typeparam name = "T"></typeparam>
        ///<typeparam name = "TKey"></typeparam>
        ///<returns></returns>
        public static IEnumerable<T> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> expression)
        {
            return source == null ? Enumerable.Empty<T>() : source.GroupBy(expression).Select(i => i.First());
        }

        ///<summary>
        ///	从集合中移除匹配项
        ///</summary>
        /// <typeparam name = "T">集合类型</typeparam>
        ///<param name = "source">集合</param>
        ///<param name = "match">谓语</param>
        ///<exception cref = "ArgumentNullException"></exception>
        public static IEnumerable<T> Remove<T>(this IEnumerable<T> source, T match)
        {
            Guard.NotNull(source, "source");
            
            var result = source.ToList();
            result.Remove(match);
            return result;
        }

        ///<summary>
        ///	从集合中移除匹配项
        ///</summary>
        /// <typeparam name = "T">集合类型</typeparam>
        ///<param name = "source">集合</param>
        ///<param name = "predicate">谓语</param>
        ///<exception cref = "ArgumentNullException"></exception>
        public static IEnumerable<T> Remove<T>(this IEnumerable<T> source, Predicate<T> predicate)
        {
            Guard.NotNull(source, "source");

            var deleteList = source.Where(child => predicate(child)).ToList();
            var result = source.ToList();
            deleteList.ForEach(t => result.Remove(t));
            return result;
        }

        ///<summary>
        ///	移除集合中项目 
        ///</summary>
        ///<param name = "source"></param>
        ///<param name = "matches"></param>
        ///<typeparam name = "T"></typeparam>
        ///<returns></returns>
        public static IEnumerable<T> RemoveAll<T>(this IEnumerable<T> source, IEnumerable<T> matches)
        {
            Guard.NotNull(source,"source");

            foreach (var m in matches)
            {
                source.Remove(m);
            }

            return source;
        }

        ///<summary>
        /// 将集合对象转换成以指定分隔符分隔的字符串
        ///</summary>
        ///<param name="source"></param>
        ///<param name="separator"></param>
        ///<typeparam name="T"></typeparam>
        ///<returns></returns>
        /// <example>
        /// 	<code>
        /// 		var values = new[] { 1, 2, 3, 4, 5 };
        ///			string csv = values.ToStringWithSeparator(';');
        /// 	</code>
        /// </example>
        public static string ToStringWithSeparator<T>(this IEnumerable<T> source, char separator)
        {
            if (source == null)
                return string.Empty;

            var csv = new StringBuilder();
            source.ForEach(value => csv.AppendFormat("{0}{1}", value, separator));
            return csv.ToString(0, csv.Length - 1);
        }

        ///<summary>
        /// 将集合对象转换成以逗号分隔的字符串
        ///</summary>
        ///<param name="source"></param>
        ///<typeparam name="T"></typeparam>
        ///<returns></returns>
        /// <example>
        /// 	<code>
        /// 		var values = new[] {1, 2, 3, 4, 5};
        ///			string csv = values.ToStringWithComma();
        /// 	</code>
        /// </example>
        public static string ToStringWithComma<T>(this IEnumerable<T> source)
        {
            return source == null ? string.Empty : source.ToStringWithSeparator(',');
        }

        #region AsGenericEnumerable
        private class GenericEnumerable<T> : IEnumerable<T>
        {
            private readonly IEnumerable _source;

            /// <summary>
            /// Initializes a new instance of the <see cref="GenericEnumerable{T}"/> class.
            /// </summary>
            /// <param name="source">The source.</param>
            public GenericEnumerable(IEnumerable source)
            {
                this._source = source;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return this._source.GetEnumerator();
            }

            IEnumerator<T> IEnumerable<T>.GetEnumerator()
            {
                return this._source.Cast<T>().GetEnumerator();
            }
        }

        /// <summary>
        /// 转泛型，对匿名对象非常有用
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        public static IEnumerable AsGenericEnumerable(this IEnumerable source)
        {
            Type elementType = typeof(Object);

            Type type = source.GetType().FindGenericType(typeof(IEnumerable<>));
            if (type != null)
            {
                return source;
            }

            IEnumerator enumerator = source.GetEnumerator();

            while (enumerator.MoveNext())
            {
                if (enumerator.Current != null)
                {
                    elementType = enumerator.Current.GetType();
                    try
                    {
                        enumerator.Reset();
                    }
                    catch
                    { }
                    break;
                }
            }

            Type genericType = typeof(GenericEnumerable<>).MakeGenericType(elementType);
            object[] constructorParameters = new object[] { source };

            return (IEnumerable)Activator.CreateInstance(genericType, constructorParameters);
        }
        #endregion

        /// <summary>
        /// 返回对象在集合索引值
        /// </summary>
        /// <param name="source"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        public static int IndexOf(this IEnumerable source, object item)
        {
            int index = 0;
            foreach (object i in source)
            {
                if (Equals(i, item))
                {
                    return index;
                }

                index++;
            }

            return -1;
        }


        /// <summary>
        /// 返回集合索引位置的值
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <exception cref="ArgumentOutOfRangeException"><c>index</c> is out of range.</exception>
        /// <returns></returns>
        public static object ElementAt(this IEnumerable source, int index)
        {
            if (index < 0)
            {
                throw new ArgumentOutOfRangeException("index");
            }

            var list = source as IList;
            if (list != null && list.Count > 0)
            {
                return list[index];
            }

            foreach (var item in source)
            {
                if (index == 0)
                {
                    return item;
                }

                index--;
            }

            return null;
        }

        /*public static IEnumerable<TSource> SelectRecursive<TSource>(this IEnumerable<TSource> source,
            Func<TSource, IEnumerable<TSource>> recursiveSelector)
        {
            Stack<IEnumerator<TSource>> stack = new Stack<IEnumerator<TSource>>();
            stack.Push(source.GetEnumerator());

            try
            {
                while (stack.Count > 0)
                {
                    if (stack.Peek().MoveNext())
                    {
                        TSource current = stack.Peek().Current;

                        yield return current;

                        IEnumerable<TSource> children = recursiveSelector(current);
                        if (children != null)
                        {
                            stack.Push(children.GetEnumerator());
                        }
                    }
                    else
                    {
                        stack.Pop().Dispose();
                    }
                }
            }
            finally
            {
                while (stack.Count > 0)
                {
                    stack.Pop().Dispose();
                }
            }
        }*/
        
        public static ReadOnlyCollection<T> ToReadOnlyCollection<T>(this IEnumerable<T> sequence)
        {
            if (sequence == null)
            {
                return DefaultReadOnlyCollection<T>.Empty;
            }
            ReadOnlyCollection<T> onlys = sequence as ReadOnlyCollection<T>;
            if (onlys != null)
            {
                return onlys;
            }
            return new ReadOnlyCollection<T>(sequence.ToArray());
        }

        private static class DefaultReadOnlyCollection<T>
        {
            private static ReadOnlyCollection<T> _defaultCollection;

            public static ReadOnlyCollection<T> Empty
            {
                get { return _defaultCollection ?? (_defaultCollection = new ReadOnlyCollection<T>(new T[0])); }
            }
        }
    }
}